package com.finance.scheduler.service;

import com.finance.framework.lang.BeanUtils;
import com.finance.framework.qurtz.RedisRunningStatusAcquirer;
import com.finance.framework.qurtz.TaskDescriptor;
import com.finance.system.bean.JobStateDesciptor;
import org.apache.commons.lang.StringUtils;
import org.quartz.*;
import org.quartz.JobDetail;
import org.quartz.impl.JobDetailImpl;
import org.quartz.impl.matchers.GroupMatcher;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.util.*;

/**
 * 定时器管理类
 * 提供新增、修改、移除、启动定时任务等功能
 */
@Component
public class QuartzManager implements ApplicationContextAware {
    private Scheduler scheduler;

    @Autowired
    private RedisRunningStatusAcquirer redisRunningStatusAcquirer;

    /**
     * 直接启动定时任务
     * @param task
     */
    public void addAndScheduleJob(TaskDescriptor task){
        try {
            Class clasz = Class.forName(task.getClassName());
            this.redisRunningStatusAcquirer.rebuildQueueMap(new TriggerKey(task.getTriggerName(),task.getJobGroupName()));
            this.addAndScheduleJob(clasz,task.getJobName(),task.getJobGroupName(),task.getTriggerName(),task.getTriggerGroupName(),task.getCronExpression(),task.getDescription());
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    public void addJob(TaskDescriptor task){
        try {
            Class clasz = Class.forName(task.getClassName());
            this.addJob(clasz,task.getJobName(),task.getJobGroupName(),task.getDescription());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void addAndScheduleJob(Class<? extends Job> jobClass, String jobName, String groupName, String triggerName, String triggerGroup, String cronExpress,String description){
        try {
            //创建job
            JobDetailImpl jobDetail = new JobDetailImpl();
            jobDetail.setName(jobName);
            jobDetail.setGroup(groupName);
            jobDetail.setJobClass(jobClass);
            jobDetail.setDescription(description);
            //创建触发器
            CronTriggerImpl trigger = new CronTriggerImpl();
            trigger.setName(StringUtils.isBlank(triggerName) ? jobName + "_trigger" : triggerName);
            trigger.setJobName(jobName);
            trigger.setJobGroup(groupName);
            trigger.setGroup(triggerGroup);
            trigger.setCronExpression(cronExpress);
            scheduler.scheduleJob(jobDetail, trigger);
            // 启动
            if (!scheduler.isShutdown()) {
                scheduler.start();
            }
        }catch(Exception e){
            throw new RuntimeException(e);
        }
    }

    private void addJob(Class<? extends Job> jobClass, String jobName, String groupName,String description) throws SchedulerException {
             //创建job
            JobDetailImpl jobDetail = new JobDetailImpl();
            jobDetail.setName(jobName);
            jobDetail.setGroup(groupName);
            jobDetail.setJobClass(jobClass);
            jobDetail.setDurability(true);
            jobDetail.setDescription(description);
            scheduler.addJob(jobDetail,true);
    }

/*
    /**
     * @param jobName
     * @param time
     * @Description: 修改一个任务的触发时间(使用默认的任务组名，触发器名，触发器组名)
     */
/*
   public  void modifyJobTime(String jobName, String jobGroup, String triggerGroup, String time) {
        try {
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(new TriggerKey(jobName, triggerGroup)); //.getTrigger(jobName,TRIGGER_GROUP_NAME);
            if (trigger == null) {
                return;
            }
            String oldTime = trigger.getCronExpression();
            if (!oldTime.equalsIgnoreCase(time)) {
                JobDetail jobDetail = scheduler.getJobDetail(new JobKey(jobName, jobGroup)); //getJobDetail(jobName,JOB_GROUP_NAME);
                Class objJobClass = jobDetail.getJobClass();
                removeJob(jobName,triggerGroup);
                addAndScheduleJob(objJobClass,jobName,jobGroup,trigger.getKey().getName(),triggerGroup,trigger.getCronExpression());
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
*/

    /**
     * @param triggerName
     * @param triggerGroupName
     * @param time
     * @Description: 修改一个任务的触发时间
     */
    public  void modifyTriggerTime(String triggerName,
                                   String triggerGroupName, String triggerGroup, String time) {
        try {
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(new TriggerKey(triggerName, triggerGroup));
            if (trigger == null) {
                return;
            }
            String oldTime = trigger.getCronExpression();
            if (!oldTime.equalsIgnoreCase(time)) {
                CronTrigger ct = (CronTrigger) trigger;
                // 修改时间
                ct.getTriggerBuilder().withIdentity(triggerName, triggerGroupName).withSchedule(CronScheduleBuilder.cronSchedule(time));
                // 重启触发器
                scheduler.resumeTrigger(new TriggerKey(triggerName, triggerGroupName));//.resumeTrigger(triggerName, triggerGroupName);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @param taskDescriptor
     * @Description: 移除一个任务(使用默认的任务组名，触发器名，触发器组名)
     */
    public  void removeJob(TaskDescriptor taskDescriptor) {
        if(StringUtils.isNotBlank(taskDescriptor.getTriggerName())){
            try {
                TriggerKey triggerKey = new TriggerKey(taskDescriptor.getTriggerName(), taskDescriptor.getTriggerGroupName());
                scheduler.pauseTrigger(triggerKey);// 停止触发器
                scheduler.unscheduleJob(triggerKey);// 移除触发器
                scheduler.deleteJob(new JobKey(taskDescriptor.getJobName(), taskDescriptor.getJobGroupName())); // 删除任务
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }else{
            try {
                scheduler.deleteJob(new JobKey(taskDescriptor.getJobName(), taskDescriptor.getJobGroupName())); // 删除任务
            } catch (SchedulerException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 启动所有定时任务
     */
    public  void startJobs() {
        try {
            scheduler.start();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void startJob(TaskDescriptor taskDiscriptor){
        try {
            Trigger trigger = this.scheduler.getTrigger(new TriggerKey(taskDiscriptor.getTriggerName(),taskDiscriptor.getTriggerGroupName()));
            if(trigger ==null){
                trigger =TriggerBuilder.newTrigger().withIdentity(taskDiscriptor.getTriggerName(),taskDiscriptor.getTriggerGroupName())
                        .forJob(taskDiscriptor.getJobName(),taskDiscriptor.getJobGroupName()).withSchedule(CronScheduleBuilder.cronSchedule(taskDiscriptor.getCronExpression())).build();
                this.redisRunningStatusAcquirer.rebuildQueueMap(trigger.getKey());
                this.scheduler.scheduleJob(trigger);
            }else{
                this.scheduler.resumeTrigger(trigger.getKey());
            }
         } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void pauseJob(TaskDescriptor taskDiscriptor){
        try {
            this.scheduler.pauseJob(new JobKey(taskDiscriptor.getJobName(),taskDiscriptor.getJobGroupName()));
            this.scheduler.pauseTrigger(new TriggerKey(taskDiscriptor.getTriggerName(),taskDiscriptor.getTriggerGroupName()));
        } catch (SchedulerException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 关闭所有定时任务
     */
    public void shutdownJobs() {
        try {
            if (!scheduler.isShutdown()) {
                scheduler.shutdown();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public List<TaskDescriptor> getAllJobs() throws SchedulerException{
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
        List<TaskDescriptor> jobs = new ArrayList<TaskDescriptor>();
        for (JobKey jobKey : jobKeys) {
        List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            if(triggers != null && triggers.size()>0){
                for(Trigger trigger : triggers){
                    CronTrigger cronTrigger = (CronTrigger)trigger;
                    TaskDescriptor task = new TaskDescriptor();
                    task.setJobName(jobKey.getName());
                    task.setJobGroupName(jobKey.getGroup());
                    task.setTriggerName(trigger.getKey().getName());
                    task.setTriggerGroupName(trigger.getKey().getGroup());
                    task.setClassName(jobDetail.getJobClass().getCanonicalName());
                    task.setCronExpression(cronTrigger.getCronExpression());
                    jobs.add(task);
                }
            }else{
                TaskDescriptor task = new TaskDescriptor();
                task.setJobName(jobKey.getName());
                task.setJobGroupName(jobKey.getGroup());
                task.setClassName(jobDetail.getJobClass().getCanonicalName());
                jobs.add(task);
            }
        }
        return jobs;
    }

    public List<JobStateDesciptor> getAllJobStateDesciptor() throws SchedulerException, InvocationTargetException, IllegalAccessException {
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
        List<JobStateDesciptor> jobs = new ArrayList<JobStateDesciptor>();
        for (JobKey jobKey : jobKeys) {
        List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            if(triggers!=null&&triggers.size()>0){
                for(Trigger trigger : triggers){
                    JobStateDesciptor jobStateDesciptor = new JobStateDesciptor();
                    CronTrigger cronTrigger = (CronTrigger)trigger;
                    BeanUtils.copyProperties(jobStateDesciptor,trigger);
                    jobStateDesciptor.setJobName(jobDetail.getKey().getName());
                    jobStateDesciptor.setJobGroupName(jobDetail.getKey().getGroup());
                    jobStateDesciptor.setClassName(jobDetail.getJobClass().getCanonicalName());
                    jobStateDesciptor.setCronExpression(cronTrigger.getCronExpression());
                    jobStateDesciptor.setTriggerName(trigger.getKey().getName());
                    jobStateDesciptor.setTriggerGroupName(trigger.getKey().getGroup());
                    jobStateDesciptor.setTriggerState(this.scheduler.getTriggerState(trigger.getKey()));
                    jobs.add(jobStateDesciptor);

                }
            }else{
                JobStateDesciptor jobStateDesciptor = new JobStateDesciptor();
                jobStateDesciptor.setJobName(jobDetail.getKey().getName());
                jobStateDesciptor.setJobGroupName(jobDetail.getKey().getGroup());
                jobStateDesciptor.setClassName(jobDetail.getJobClass().getCanonicalName());
                jobs.add(jobStateDesciptor);
            }
        }
        return jobs;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.scheduler = (Scheduler)applicationContext.getBean("schedulerFactory");
    }

}